import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class Demo1024 extends JPanel{
    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private final int width = 98;
	private final int height = 98;
	private int[][] cards = new int[4][4];
	private int cardSum = 0;
	private boolean over = false;
	private boolean isCardMove = false;
	
	
	public static void main(String[] args) {
		JFrame jf = new JFrame("1024demo");
		jf.setBackground(Color.GRAY);
		jf.setSize(405,430);
		Demo1024 d = new Demo1024();
		jf.add(d);
		jf.setResizable(false);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setVisible(true);
		d.action(jf);
	}


	@Override
	public void paint(Graphics g) {
		super.paint(g);
		g.setColor(Color.BLACK);
		Font f = g.getFont();
		if(cardSum==0){
			g.setFont(new Font(f.getFontName(),Font.BOLD,48));
			g.drawString("S to start", 80, 150);
			g.drawString("Esc to exit", 80, 200);
			return;
		}
		if(over){
			g.setFont(new Font(f.getFontName(),Font.BOLD,48));
			g.drawString("OVER", 120, 120);
			g.setFont(new Font(f.getFontName(),Font.BOLD,28));
			g.drawString("R to restart", 120, 200);
			g.drawString("Esc to exit", 120, 250);
			return;
		}
		g.setFont(f);
		g.drawLine(0, 100, 400, 100);
		g.drawLine(0, 200, 400, 200);
		g.drawLine(0, 300, 400, 300);
		g.drawLine(100, 0, 100, 400);
		g.drawLine(200, 0, 200, 400);
		g.drawLine(300, 0, 300, 400);
		paintCard(g);
	}

	private void paintCard(Graphics g) {
		for(int i=0;i<4;i++){
			for(int j=0;j<4;j++){
				int card = cards[i][j];
				if(card!=0){
					g.setColor(Color.YELLOW);
					g.fillRect(i*100+1, j*100+1, width, height);
					g.setColor(Color.BLACK);
					g.setFont(new Font(null,Font.BOLD,30));
					g.drawString(""+card, i*100+30, j*100+60);
				}
			}
		}
	}


	private void action(JFrame jf) {
		jf.addKeyListener(new KeyAdapter(){

			@Override
			public void keyPressed(KeyEvent e) {
				switch (e.getKeyCode()) {
				case KeyEvent.VK_UP:moveUp();
					break;
					
				case KeyEvent.VK_DOWN:moveDown();
					break;
					
				case KeyEvent.VK_LEFT:moveLeft();
					break;
					
				case KeyEvent.VK_RIGHT:moveRight();
					break;
					
				case KeyEvent.VK_ESCAPE:close();
					break;

				case KeyEvent.VK_R:restart();
					break;
				
				case KeyEvent.VK_S:start();
					break;
					
				default:
					break;
				}
				
				if(isGameOver())over=true;
				if(e.getKeyCode()!=83&&e.getKeyCode()!=82&&isCardMove){
					getNewNumber();
					isCardMove=false;
				}
				repaint();
				
			}
			
		});
		jf.addMouseListener(new MouseAdapter(){
			Point p1;
			Point p2;
			@Override
			public void mousePressed(MouseEvent e) {
				p1 = e.getPoint();
			}
			@Override
			public void mouseReleased(MouseEvent e) {
				p2 = e.getPoint();
				double x = p1.getX()-p2.getX();
				double y = p1.getY()-p2.getY();
				if(x*x>y*y && x>0)moveLeft();
				if(x*x>y*y && x<0)moveRight();
				if(x*x<y*y && y>0)moveUp();
				if(x*x<y*y && y<0)moveDown();
				
				if(isGameOver())over=true;
				if(isCardMove){
					getNewNumber();
					isCardMove=false;
				}
				repaint();
			}
			
		});
	}


	protected boolean isGameOver() {
		if(cardSum<16)return false;
		for(int i=0;i<4;i++){
			for(int j=0;j<3;j++){
				if(cards[i][j]==cards[i][j+1])return false;
			}
		}
		for(int j=0;j<4;j++){
			for(int i=0;i<3;i++){
				if(cards[i][j]==cards[i+1][j])return false;
			}
		}
		return true;
	}


	protected void moveRight() {
		for(int j=0;j<4;j++){
			int[] c = new int[4];
			int k =3;
			for(int i=3;i>=0;i--){
				if(cards[i][j]!=0){
					c[k--] = cards[i][j];
					cards[i][j] = 0;
					if(i != (k+1)){
						isCardMove=true;
					}
				}
			}
			for(int i=3;i>0;i--){
				if(c[i]!=0&&c[i]==c[i-1]){
					c[i]=c[i]*2;
					c[i-1]=0;
					isCardMove=true;
					cardSum--;
				}
			}
			int x=3;
			for(int i=3;i>=0;i--){
				if(c[i]!=0){
					cards[x--][j]=c[i];
				}
			}
		}
	}


	protected void moveLeft() {
		for(int j=0;j<4;j++){
			int[] c = new int[4];
			int k =0;
			for(int i=0;i<4;i++){
				if(cards[i][j]!=0){
					c[k++] = cards[i][j];
					cards[i][j] = 0;
					if(i != (k-1)){
						isCardMove=true;
					}
				}
			}
			for(int i=0;i<3;i++){
				if(c[i]!=0&&c[i]==c[i+1]){
					c[i]=c[i]*2;
					c[i+1]=0;
					isCardMove=true;
					cardSum--;
				}
			}
			int x=0;
			for(int i=0;i<4;i++){
				if(c[i]!=0){
					cards[x++][j]=c[i];
				}
			}
		}
	}


	protected void moveUp() {
		for(int i=0;i<4;i++){
			int[] c = new int[4];
			int k =0;
			for(int j=0;j<4;j++){
				if(cards[i][j]!=0){
					c[k++] = cards[i][j];
					cards[i][j] = 0;
					if(j != (k-1)){
						isCardMove=true;
					}
				}
			}
			for(int j=0;j<3;j++){
				if(c[j]!=0&&c[j]==c[j+1]){
					c[j]=c[j]*2;
					c[j+1]=0;
					isCardMove=true;
					cardSum--;
				}
			}
			int y=0;
			for(int j=0;j<4;j++){
				if(c[j]!=0){
					cards[i][y++]=c[j];
				}
			}
		}
	}


	protected void moveDown() {
		for(int i=0;i<4;i++){
			int[] c = new int[4];
			int k =3;
			for(int j=3;j>=0;j--){
				if(cards[i][j]!=0){
					c[k--] = cards[i][j];
					cards[i][j] = 0;
					if(j != (k+1)){
						isCardMove=true;
					}
				}
			}
			for(int j=3;j>0;j--){
				if(c[j]!=0&&c[j]==c[j-1]){
					c[j]=c[j]*2;
					c[j-1]=0;
					isCardMove=true;
					cardSum--;
				}
			}
			int y=3;
			for(int j=3;j>=0;j--){
				if(c[j]!=0){
					cards[i][y--]=c[j];
				}
			}
		}
	}


	protected void close() {
		System.exit(0);
	}


	protected void restart() {
		cards = new int[4][4];
		cardSum = 0;
		over = false;
		isCardMove = false;
		start();
	}


	protected void start() {
		while(cardSum<4){
			getNewNumber();
		}
	}


	private void getNewNumber() {
		Random r = new Random();
		int num = r.nextInt(10)>2?2:4;
		while(cardSum<16){
			int i = r.nextInt(4);
			int j = r.nextInt(4);
			if(cards[i][j]==0){
				cards[i][j]=num;
				cardSum++;
				break;
			}
		}
	}
}
